package cn.starrysky108.simpleerp.pub.controller;

import cn.starrysky108.simpleerp.core.base.BaseResponseEntity;
import cn.starrysky108.simpleerp.core.security.UserDetailsEntity;
import cn.starrysky108.simpleerp.core.util.JwtUtil;
import cn.starrysky108.simpleerp.pub.request.RefreshTokenRequest;
import cn.starrysky108.simpleerp.pub.request.UserRegisterRequest;
import cn.starrysky108.simpleerp.pub.response.TokenResponse;
import cn.starrysky108.simpleerp.pub.service.AuthService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.integration.redis.util.RedisLockRegistry;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.util.Assert;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.annotations.ApiIgnore;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotNull;
import java.util.concurrent.locks.Lock;

/**
 * 认证相关Api
 *
 * @author Carl Lee
 */
@RestController
@RequestMapping("/auth")
@Api("认证Api")
@Slf4j
public class AuthController {
    private final AuthService authService;
    private final UserDetailsService userDetailsService;
    private final RedisLockRegistry redisLockRegistry;

    public AuthController(AuthService authService, UserDetailsService userDetailsService, RedisLockRegistry redisLockRegistry) {
        this.authService = authService;
        this.userDetailsService = userDetailsService;
        this.redisLockRegistry = redisLockRegistry;
    }

    @PostMapping("/logout")
    @ApiOperation("退出登录")
    @ApiImplicitParam(name = "refreshTokenRequest", dataTypeClass = RefreshTokenRequest.class, required = true, paramType = "body")
    public BaseResponseEntity<String> logout(@NotNull @RequestBody RefreshTokenRequest refreshTokenRequest,
                                             @ApiIgnore HttpServletRequest request, @ApiIgnore HttpServletResponse response, @ApiIgnore Authentication authentication) {
        //退出登录
        new SecurityContextLogoutHandler().logout(request, response, authentication);
        String refreshToken = JwtUtil.normalizeToken(refreshTokenRequest.getRefreshToken());
        authService.logout(refreshToken);
        return BaseResponseEntity.success(refreshToken);
    }

    @PostMapping("/register")
    @ApiOperation("注册")
    public BaseResponseEntity<TokenResponse> register(@Validated @RequestBody UserRegisterRequest user) {
        Assert.notNull(user, "user对象为空");
        Lock lock = redisLockRegistry.obtain(user.getUsername());
        TokenResponse tokenResponse = null;
        lock.lock();
        try {
            if (authService.hasUser(user.getUsername())) {
                return BaseResponseEntity.failure("用户名已被使用，请重新输入");
            }
            tokenResponse = authService.register(user);

        } catch (IllegalStateException e) {
            log.error(e.getMessage());
        } catch (Exception e) {
            log.error(e.getMessage());
            return BaseResponseEntity.failure("注册失败");
        } finally {
            lock.unlock();
        }
        UserDetailsEntity userDetails = (UserDetailsEntity) userDetailsService.loadUserByUsername(user.getUsername());
        UsernamePasswordAuthenticationToken token
                = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(token);
        return BaseResponseEntity.success(tokenResponse);
    }

    @PostMapping("/token")
    @ApiOperation(value = "获取业务token")
    public BaseResponseEntity<TokenResponse> getRefreshToken(@RequestBody RefreshTokenRequest refreshTokenRequest) {
        //验证refresh_token的合法性
        Assert.notNull(refreshTokenRequest.getRefreshToken(), "refreshToken不能为空");
        //规范化token
        refreshTokenRequest.setRefreshToken(JwtUtil.normalizeToken(refreshTokenRequest.getRefreshToken()));
        refreshTokenRequest.setAccessToken(JwtUtil.normalizeToken(refreshTokenRequest.getAccessToken()));
        JwtUtil.verifyRefreshToken(refreshTokenRequest.getRefreshToken());
        return BaseResponseEntity.success(authService.getToken(refreshTokenRequest));
    }
}
